// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <fstream>
#include <iostream>

#include "src/interpreter/bytecodes.h"

namespace v8 {
namespace internal {
    namespace interpreter {

        void WriteBytecode(std::ofstream& out, Bytecode bytecode,
            OperandScale operand_scale, int* count, int offset_table[],
            int table_index)
        {
            DCHECK_NOT_NULL(count);
            if (Bytecodes::BytecodeHasHandler(bytecode, operand_scale)) {
                out << " \\\n  V(" << Bytecodes::ToString(bytecode, operand_scale, "")
                    << "Handler, interpreter::OperandScale::k" << operand_scale
                    << ", interpreter::Bytecode::k" << Bytecodes::ToString(bytecode) << ")";
                offset_table[table_index] = *count;
                (*count)++;
            } else {
                offset_table[table_index] = -1;
            }
        }

        void WriteHeader(const char* header_filename)
        {
            std::ofstream out(header_filename);

            out << "// Automatically generated from interpreter/bytecodes.h\n"
                << "// The following list macro is used to populate the builtins list\n"
                << "// with the bytecode handlers\n\n"
                << "#ifndef V8_BUILTINS_GENERATED_BYTECODES_BUILTINS_LIST\n"
                << "#define V8_BUILTINS_GENERATED_BYTECODES_BUILTINS_LIST\n\n"
                << "namespace v8 {\n"
                << "namespace internal {\n\n"
                << "#define BUILTIN_LIST_BYTECODE_HANDLERS(V)";

            constexpr int kTableSize = BytecodeOperands::kOperandScaleCount * Bytecodes::kBytecodeCount;
            int offset_table[kTableSize];
            int count = 0;
            int index = 0;

#define ADD_BYTECODES(Name, ...)                                               \
    WriteBytecode(out, Bytecode::k##Name, operand_scale, &count, offset_table, \
        index++);
            OperandScale operand_scale = OperandScale::kSingle;
            BYTECODE_LIST(ADD_BYTECODES)
            int single_count = count;
            operand_scale = OperandScale::kDouble;
            BYTECODE_LIST(ADD_BYTECODES)
            int wide_count = count - single_count;
            operand_scale = OperandScale::kQuadruple;
            BYTECODE_LIST(ADD_BYTECODES)
#undef ADD_BYTECODES
            int extra_wide_count = count - wide_count - single_count;
            CHECK_GT(single_count, wide_count);
            CHECK_EQ(single_count, Bytecodes::kBytecodeCount);
            CHECK_EQ(wide_count, extra_wide_count);
            out << "\n\nconst int kNumberOfBytecodeHandlers = " << single_count << ";\n"
                << "const int kNumberOfWideBytecodeHandlers = " << wide_count << ";\n\n"
                << "// Mapping from (Bytecode + OperandScaleAsIndex * |Bytecodes|) to\n"
                << "// a dense form with all the illegal Bytecode/OperandScale\n"
                << "// combinations removed. Used to index into the builtins table.\n"
                << "constexpr int kBytecodeToBuiltinsMapping[" << kTableSize << "] = {\n"
                << "    ";

            for (int i = 0; i < kTableSize; ++i) {
                if (i == single_count || i == 2 * single_count) {
                    out << "\n    ";
                }
                out << offset_table[i] << ", ";
            }

            out << "};\n\n"
                << "}  // namespace internal\n"
                << "}  // namespace v8\n"
                << "#endif  // V8_BUILTINS_GENERATED_BYTECODES_BUILTINS_LIST\n";
        }

    } // namespace interpreter
} // namespace internal
} // namespace v8

// int main(int argc, const char* argv[])
// {
//     if (argc != 2) {
//         std::cerr << "Usage: " << argv[0] << " <output filename>\n";
//         std::exit(1);
//     }
// 
//     v8::internal::interpreter::WriteHeader(argv[1]);
// 
//     return 0;
// }
